<?php

namespace app\service;

use app\model\OrderDao;
use app\model\UserDao;
use app\model\WechatLogDao;
use think\facade\Db;
use think\facade\Log;
use think\facade\Request;

class WechatPay
{

    private function send(string $method, string $url, string $data,?callable $callback=null): array
    {
        $start = microtime(true);
        $log = new WechatLogDao();
        $log->save([
            'url' => $url,
            'request' => $data,
            'ip' => Request::ip(),
            'uid' => request()->uid,
        ]);
        if (strtoupper($method) === 'POST') {
            $ret = $this->curl_post($url, $data);
        } else {
            $ret = curl_get($url);
        }
        $time = (microtime(true) - $start) * 1000;
        $log->save([
            'response' => $ret,
            'time' => $time,
        ]);
        return [$ret, $log->id];
    }

    /**
     * 物流公司列表
     * @link  https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/express/business/express_search.html#%E8%8E%B7%E5%8F%96%E8%BF%90%E5%8A%9Bid%E5%88%97%E8%A1%A8get-delivery-list
     * @return void
     */
    public function delivery_list()
    {
        $appId = config('wechat.app_id');
        $secret = config('wechat.secret');
        $token = $this->getAccessToken();
        $url = 'https://api.weixin.qq.com/cgi-bin/express/delivery/open_msg/get_delivery_list?access_token=' . $token;
        list($result, $logId) = $this->send('POST', $url, '｛｝');
        return $result;
    }


    /**
     * 发货
     * @param $order
     * @return string
     * @link  https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html
     */
    //订单发货
    public function delivery(OrderDao $order)
    {
        $data = [];
        //订单单号类型，用于确认需要上传详情的订单。
        //  1，使用下单商户号和商户侧单号；
        //  2，使用微信支付单号。
        $data['order_key']['order_number_type'] = 2;
        $data['order_key']['transaction_id'] = $order->transaction_id;
        //物流模式，发货方式枚举值：
        //   1、实体物流配送采用快递公司进行实体物流配送形式
        //   2、同城配送
        //   3、虚拟商品，虚拟商品，例如话费充值，点卡等，无实体配送形式
        //   4、用户自提
        $data['logistics_type'] = 1;
        //发货模式，发货模式枚举值：
        //1、UNIFIED_DELIVERY（统一发货）
        //2、SPLIT_DELIVERY（分拆发货）
        $data['delivery_mode'] = 1;
        $data['shipping_list'][0]['tracking_no'] = $order['delivery_no'];
        $data['shipping_list'][0]['express_company'] = $order['delivery_code'];
        $data['shipping_list'][0]['item_desc'] = $order['goods_name'] ?? '商品';
        $data['shipping_list'][0]['contact']['receiver_contact'] = substr($order['mobile'], 0, 3) . '****' . substr($order['mobile'], -4);
        $data['upload_time'] = date(DATE_RFC3339);
        $data['payer']['openid'] = UserDao::where('id', $order['uid'])->value('openid');
        $url = 'https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token=' . $this->getAccessToken();
        list($rs, $logId) = $this->send('POST', $url, json_encode($data, JSON_UNESCAPED_UNICODE));
        $ret = json_decode($rs);
        if (!empty($ret->errmsg)) {
            WechatLogDao::where('id', $logId)->save([
                'code' => $ret->errcode,
                'message' => $ret->errmsg,
            ]);
            show_error($ret->errmsg);
        }
        return $ret;
    }

    //虚拟发货
    public function virtualDelivery(OrderDao &$order): string
    {
        try {
            if (empty($order->transaction_id)) {
                return sprintf("[%s][%d]无需发货", $order->order_num, $order->id);
            }
            $data = [];
            $uid = $order->uid;
            $openId = UserDao::where('id', $uid)->value('openid');
            $title = $order->type == 1 ? '购买会员' : '服务咨询';//订单单号类型，用于确认需要上传详情的订单。
            //枚举值1，使用下单商户号和商户侧单号；
            //枚举值2，使用微信支付单号。
            $data['order_key']['order_number_type'] = 2;//原支付交易对应的微信订单号
            $data['order_key']['transaction_id'] = $order->transaction_id;//物流模式，发货方式枚举值：
            // 1、实体物流配送采用快递公司进行实体物流配送形式
            // 2、同城配送
            // 3、虚拟商品，虚拟商品，例如话费充值，点卡等，无实体配送形式
            // 4、用户自提
            $data['logistics_type'] = 3;//发货模式，发货模式枚举值：
            //1、UNIFIED_DELIVERY（统一发货）
            //2、SPLIT_DELIVERY（分拆发货）
            $data['delivery_mode'] = 1;//$data['shipping_list'][0]['tracking_no'] = $order['logistics_num'];
            //$data['shipping_list'][0]['express_company'] = $order['logistics_name'];
            $data['shipping_list'][0]['item_desc'] = $title;//$data['shipping_list'][0]['contact']['receiver_contact'] = substr($order['mobile'], 0, 3) . '****' . substr($order['mobile'], -4);
            $data['upload_time'] = date(DATE_RFC3339);
            $data['payer']['openid'] = $openId;
            $url = 'https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token=' . $this->getAccessToken();
            list($result, $logId) = $this->send('POST', $url, json_encode($data, JSON_UNESCAPED_UNICODE));
            $result = json_decode($result, true);
            WechatLogDao::where('id', $logId)->update([
                'code' => $result['errcode'] ?? 'FAIL'
            ]);
            return $result['errcode'] == 0 ? '' : ($result['errmsg'] ?? 'FAILED');
        } catch (\Exception $e) {
            trace_file($e);
            return $e->getMessage();
        }
    }

    public function getAccessToken(?string $appId = null, ?string $secret = null): string
    {
        $appId = $appId ?? config('wechat.app_id');
        $secret = $secret ?? config('wechat.secret');
        //$appid = config('wechat.app_id');
        $key = $appId . ':access_token';
        $accessToken = cache($key);
        if (!empty($accessToken)) {
            return $accessToken;
        }
        //$secret = config('wechat.secret');
        $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $appId . '&secret=' . $secret;
        $res = http_get($url);
        $res = json_decode($res, true);
        if (!empty($res['access_token'])) {
            $accessToken = $res['access_token'];
            cache($key, $accessToken, 7000);
            return $accessToken;
        }
        show_error(!empty($res['errmsg']) ? $res['errmsg'] : 'access_token获取失败');
    }

    /**
     * 退款
     * @link https://pay.weixin.qq.com/doc/v2/merchant/4011935217
     */
    public function refund(OrderDao $order, $refund_money)
    {
        $url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
        $postData = [
            'appid' => config('wechat.app_id'),
            'mch_id' => config('wechat.mchid'),
            'nonce_str' => Weixin::randomkeys(16),
            //'transaction_id' => $order->transaction_id,
            'out_refund_no' => 'refund_' . $order->uid . time(),
            'total_fee' => intval($order->pay_money * 100),
            'refund_fee' => intval($refund_money * 100),
            //'sign_type'=>'MD5',//默认md5
        ];
        if ($order->transaction_id) {
            $postData['transaction_id'] = $order->transaction_id;
        } else {
            $postData['out_trade_no'] = $order->order_num;
        }
        $postData['sign'] = Weixin::MakeSign($postData);
        $xml = Weixin::ToXml($postData);
        list($rs, $logId) = $this->send('POST', $url, $xml);
        $ret = Weixin::fromXml($rs);
        if (!empty($ret['err_code_des'])) {
            WechatLogDao::where('id', $logId)->update([
                'code' => $ret['err_code'] ?? 'FAIL',
                'msg' => $ret['err_code_des'],
            ]);
        } elseif (!empty($ret['result_code'])) {
            WechatLogDao::where('id', $logId)->update([
                'code' => $ret['result_code'],
                'msg' => $ret['return_code'] ?? null,
            ]);
        }
        return $ret;
    }


    function curl_post(string $url, string $data,callable $callback = null)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        //$cert = './public/cert/apiclient_cert.pem';
        $certPath = syspath('public/cert/apiclient_cert.pem');
        //$key = './public/cert/apiclient_key.pem';
        $keyPath = syspath('public/cert/apiclient_key.pem');
        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLCERT, $certPath);
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, $keyPath);
        if ($callback) {
            $callback($ch);
        }
        $info = curl_exec($ch);
        if ($errno = curl_errno($ch)) {
            $msg = curl_strerror($errno);
            show_error('CURL:' . $msg);
        }
        curl_close($ch);
        return $info;
    }

    function curl_get($url)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
        $info = curl_exec($curl);
        curl_close($curl);
        return $info;
    }

}